#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <alloca.h>
#include <unistd.h>
#include <dlfcn.h>
#include "plugin.h"
#include "hashtbl.h"

#define ENV_VAR		"RENDY_PLUGIN_PATH"

#if defined(__APPLE__) && defined(__MACH__)
#define SUFFIX	".dylib"
#else
#define SUFFIX	".so"
#endif

static struct hash_table *so_cache;

int set_plugin_path(const char *path)
{
	char *env_str = alloca(strlen(ENV_VAR) + strlen(path) + 2);
	sprintf(env_str, ENV_VAR "=%s", path);
	return putenv(env_str);
}

const char *get_plugin_path(void)
{
	return getenv(ENV_VAR);
}

int add_plugin_dir(const char *dirname)
{
	char *path, *cur_path;
	
	if(!(cur_path = getenv(ENV_VAR))) {
		cur_path = "";
	}
	path = alloca(strlen(cur_path) + strlen(dirname) + 2);
	sprintf(path, "%s:%s", cur_path, dirname);

	return set_plugin_path(path);
}

void *get_plugin(const char *name)
{
	char *path, *fname, *ptr;
	void *so = 0;

	if(so_cache || (so_cache = htab_create(htab_hash_string, htab_cmp_string))) {
		if((so = htab_get(so_cache, (void*)name))) {
			return so;
		}
	}

	if(!(path = getenv(ENV_VAR))) {
		path = "";
	}
	fname = alloca(strlen(name) + strlen(path) + 2);

	while(*path) {
		ptr = fname;
		while(*path && *path != ':') {
			*ptr++ = *path++;
		}
		if(*path == ':') {
			path++;
		}
		sprintf(ptr, "/%s" SUFFIX, name);
		
		if((so = dlopen(fname, RTLD_LAZY))) {
			break;
		}
	}

	if(so && so_cache) {
		/* XXX if the hash table was ever to be destroyed, this key string would leak
		 * however, it's meant to live during the whole lifetime of the raytracer.
		 */
		htab_add(so_cache, strdup(name), so);
	}
	return so;
}
